using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;
using System.IO;
using System.Security;
using Konst3d.Efficiency.Suite;

namespace Konst3d.Efficiency.Suite.SparsedBinary
{
    public class SparseBinaryStorage: IEfficiencySuite
    {
        #region Constants

        private const string MONITORS_FOLDER_NAME = "monitors";
        private const string MILESTONES_FOLDER_NAME = "milestones";
        private const string MILESTONE_FILE_NAME = "milestones";
        private const string TASKS_FOLDER_NAME = "tasks";
        private const string TASKS_FILE_NAME = "tasks";
        private const string COUNTDOWNS_FOLDER_NAME = "countdowns";
        private const string COUNTDOWNS_FILE_NAME = "counters";
        private const string MONITORING_GROUPS_FOLDER_NAME = "monitoring groups";
        private const string BUYS_FOLDER_NAME = "buys";
        private const string BUYS_FILE_NAME = "buys";
        private const string NOTES_FOLDER_NAME = "notes";
        private const string NOTES_FILE_NAME = "notes";

        #endregion // Constants

        #region Properties

        public IMonitorCollection Monitors
        {
            get
            {
                return _monitors;
            }
        }

        public IMilestoneCollection Milestones
        {
            get
            {
                return _milestones;
            }
        }

        public ITaskCollection Tasks
        {
            get
            {
                return _tasks;
            }
        }

        public ICountDownTimerCollection CountDownTimers
        {
            get
            {
                return _countDowns;
            }
        }

        public IMonitoringGroupCollection Groups
        {
            get
            {
                return _monitoringGroups;
            }
        }

        public IBuyCollection Buys
        {
            get
            {
                return _buys;
            }
        }

        public INoteCollection Notes
        {
            get
            {
                return _notes;
            }
        }

        public string Login
        {
            get
            {
                return _login;
            }
            set
            {
                _storageFile.LoginID = _labels.UpdateLabel(_storageFile.LoginID, value);
                _login = value;
            }
        }

        public string Password
        {
            get
            {
                return _password;
            }
            set
            {
                _storageFile.PasswordID = _labels.UpdateLabel(_storageFile.PasswordID, value);
                _password = value;
            }
        }

        public string Folder
        {
            get;
            private set;
        }

        #endregion // Properties

        #region Creation/Opening/Deleting Related Methods

        public static int GetCryptographicEngineID(string file)
        {
            return StorageFile.GetCryptographicEngineID(file);
        }

        public static bool IsPasswordProtected(string path, ICryptographicEngine cryptographicEngine)
        {
            LabelsStorage labels = null;
            StorageFile storageFile = null;

            bool hasLoginAndPass = false;

            try
            {
                labels = LabelsStorage.Open(Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path)), cryptographicEngine);
                storageFile = StorageFile.Open(path, cryptographicEngine);

                string login = labels.GetLabel(storageFile.LoginID);
                string password = labels.GetLabel(storageFile.PasswordID);

                hasLoginAndPass = !String.IsNullOrEmpty(login) || !String.IsNullOrEmpty(password);
            }
            finally
            {
                if (labels != null)
                {
                    labels.Dispose();
                }

                if (storageFile != null)
                {
                    storageFile.Dispose();
                }
            }

            return hasLoginAndPass;
        }

        public static SparseBinaryStorage Create(string path, bool overwrite, string login, string password, ICryptographicEngine cryptographicEngine, int cryptographicEngineID)
        {
            string folder = Path.GetDirectoryName(path);

            if (!Directory.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }

            StorageFile storageFile = null;
            MonitorCollectionStorage monitors = null;
            MilestoneStorage milestones = null;
            TaskStorage tasks = null;
            CountDownTimerStorage countDowns = null;
            MonitoringGroupCollectionStorage monitoringGroups = null;
            BuyCollectionStorage buys = null;
            NoteCollectionStorage notes = null;
            LabelsStorage labels = null;

            try
            {
                labels = LabelsStorage.Create(Path.Combine(folder, Path.GetFileNameWithoutExtension(path)), overwrite, cryptographicEngine);
                storageFile = StorageFile.Create(path, overwrite, cryptographicEngine, DateTime.Now, labels.AddLabel(login), labels.AddLabel(password), cryptographicEngineID);
                monitors = MonitorCollectionStorage.Create(Path.Combine(folder, MONITORS_FOLDER_NAME), cryptographicEngine);

                #region Create Milestones

                if (!Directory.Exists(Path.Combine(folder, MILESTONES_FOLDER_NAME)))
                {
                    Directory.CreateDirectory(Path.Combine(folder, MILESTONES_FOLDER_NAME));
                }

                milestones = MilestoneStorage.Create(Path.Combine(folder, MILESTONES_FOLDER_NAME, MILESTONE_FILE_NAME), overwrite, cryptographicEngine);

                #endregion // Create Milestones

                #region Create Tasks

                if (!Directory.Exists(Path.Combine(folder, TASKS_FOLDER_NAME)))
                {
                    Directory.CreateDirectory(Path.Combine(folder, TASKS_FOLDER_NAME));
                }

                tasks = TaskStorage.Create(Path.Combine(folder, TASKS_FOLDER_NAME, TASKS_FILE_NAME), overwrite, cryptographicEngine);

                #endregion // Create Tasks

                #region Create CountDowns

                if (!Directory.Exists(Path.Combine(folder, COUNTDOWNS_FOLDER_NAME)))
                {
                    Directory.CreateDirectory(Path.Combine(folder, COUNTDOWNS_FOLDER_NAME));
                }

                countDowns = CountDownTimerStorage.Create(Path.Combine(folder, COUNTDOWNS_FOLDER_NAME, COUNTDOWNS_FILE_NAME), overwrite, cryptographicEngine);

                #endregion // Create CountDowns

                #region Create MonitoringGroups

                if (!Directory.Exists(Path.Combine(folder, MONITORING_GROUPS_FOLDER_NAME)))
                {
                    Directory.CreateDirectory(Path.Combine(folder, MONITORING_GROUPS_FOLDER_NAME));
                }

                monitoringGroups = MonitoringGroupCollectionStorage.Create(Path.Combine(folder, MONITORING_GROUPS_FOLDER_NAME), monitors, tasks, overwrite, cryptographicEngine);

                #endregion // Create MonitoringGroups

                #region Create Buys

                if (!Directory.Exists(Path.Combine(folder, BUYS_FOLDER_NAME)))
                {
                    Directory.CreateDirectory(Path.Combine(folder, BUYS_FOLDER_NAME));
                }

                buys = BuyCollectionStorage.Create(Path.Combine(folder, BUYS_FOLDER_NAME, BUYS_FILE_NAME), overwrite, cryptographicEngine);

                #endregion // Create Buys

                #region Create Notes

                if (!Directory.Exists(Path.Combine(folder, NOTES_FOLDER_NAME)))
                {
                    Directory.CreateDirectory(Path.Combine(folder, NOTES_FOLDER_NAME));
                }

                notes = NoteCollectionStorage.Create(Path.Combine(folder, NOTES_FOLDER_NAME, NOTES_FILE_NAME), overwrite, cryptographicEngine);

                #endregion // Create Notes

                return new SparseBinaryStorage(monitors, milestones, tasks, countDowns, monitoringGroups, buys, notes, storageFile, labels, folder, cryptographicEngine);
            }
            catch
            {
                if (storageFile != null)
                {
                    storageFile.Delete();
                }

                if (monitors != null)
                {
                    monitors.Delete();
                }

                if (milestones != null)
                {
                    milestones.Delete();
                }

                if (tasks != null)
                {
                    tasks.Delete();
                }

                if (countDowns != null)
                {
                    countDowns.Delete();
                }

                if (monitoringGroups != null)
                {
                    monitoringGroups.Delete();
                }

                if (buys != null)
                {
                    buys.Delete();
                }

                if (notes != null)
                {
                    notes.Delete();
                }

                if (labels != null)
                {
                    labels.Delete();
                }

                throw;
            }
        }

        public static SparseBinaryStorage Open(string path, string login, string password, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(!String.IsNullOrEmpty(path));
            Contract.Requires(File.Exists(path));

            string folder = Path.GetDirectoryName(path);

            StorageFile storageFile = null;
            MonitorCollectionStorage monitors = null;
            MilestoneStorage milestones = null;
            TaskStorage tasks = null;
            CountDownTimerStorage countDowns = null;
            MonitoringGroupCollectionStorage monitoringGroups = null;
            BuyCollectionStorage buys = null;
            NoteCollectionStorage notes = null;
            LabelsStorage labels = null;

            try
            {
                labels = LabelsStorage.Open(Path.Combine(folder, Path.GetFileNameWithoutExtension(path)), cryptographicEngine);
                storageFile = StorageFile.Open(path, cryptographicEngine);

                if (labels.GetLabel(storageFile.LoginID) != login || labels.GetLabel(storageFile.PasswordID) != password)
                {
                    throw new SecurityException(@"Invalid login and\or password.");
                }

                monitors = MonitorCollectionStorage.Open(Path.Combine(folder, MONITORS_FOLDER_NAME), cryptographicEngine);
                milestones = MilestoneStorage.Open(Path.Combine(folder, MILESTONES_FOLDER_NAME, MILESTONE_FILE_NAME), cryptographicEngine);
                tasks = TaskStorage.Open(Path.Combine(folder, TASKS_FOLDER_NAME, TASKS_FILE_NAME), cryptographicEngine);
                countDowns = CountDownTimerStorage.Open(Path.Combine(folder, COUNTDOWNS_FOLDER_NAME, COUNTDOWNS_FILE_NAME), cryptographicEngine);
                monitoringGroups = MonitoringGroupCollectionStorage.Open(Path.Combine(folder, MONITORING_GROUPS_FOLDER_NAME), monitors, tasks, cryptographicEngine);
                buys = BuyCollectionStorage.Open(Path.Combine(folder, BUYS_FOLDER_NAME, BUYS_FILE_NAME), cryptographicEngine);
                notes = NoteCollectionStorage.Open(Path.Combine(folder, NOTES_FOLDER_NAME, NOTES_FILE_NAME), cryptographicEngine);

                return new SparseBinaryStorage(monitors, milestones, tasks, countDowns, monitoringGroups, buys, notes, storageFile, labels, folder, cryptographicEngine);
            }
            catch
            {
                if (storageFile != null)
                {
                    storageFile.Dispose();
                }

                if (monitors != null)
                {
                    monitors.Dispose();
                }

                if (milestones != null)
                {
                    milestones.Dispose();
                }

                if (tasks != null)
                {
                    tasks.Dispose();
                }

                if (countDowns != null)
                {
                    countDowns.Dispose();
                }

                if (monitoringGroups != null)
                {
                    monitoringGroups.Dispose();
                }

                if (buys != null)
                {
                    buys.Dispose();
                }

                if (notes != null)
                {
                    notes.Dispose();
                }

                if (labels != null)
                {
                    labels.Dispose();
                }

                throw;
            }
        }

        public void Delete()
        {
            Contract.Assert(!_disposed);

            _storageFile.Delete();
            _monitors.Delete();
            _milestones.Delete();
            _tasks.Delete();
            _countDowns.Delete();
            _monitoringGroups.Delete();
            _buys.Delete();
            _notes.Delete();
            _labels.Delete();

            if (Directory.Exists(Path.Combine(Folder, MILESTONES_FOLDER_NAME)))
            {
                Directory.Delete(Path.Combine(Folder, MILESTONES_FOLDER_NAME));
            }

            if (Directory.Exists(Path.Combine(Folder, TASKS_FOLDER_NAME)))
            {
                Directory.Delete(Path.Combine(Folder, TASKS_FOLDER_NAME));
            }

            if (Directory.Exists(Path.Combine(Folder, COUNTDOWNS_FOLDER_NAME)))
            {
                Directory.Delete(Path.Combine(Folder, COUNTDOWNS_FOLDER_NAME));
            }

            if (Directory.Exists(Path.Combine(Folder, MONITORING_GROUPS_FOLDER_NAME)))
            {
                Directory.Delete(Path.Combine(Folder, MONITORING_GROUPS_FOLDER_NAME));
            }

            if (Directory.Exists(Path.Combine(Folder, BUYS_FOLDER_NAME)))
            {
                Directory.Delete(Path.Combine(Folder, BUYS_FOLDER_NAME));
            }

            if (Directory.Exists(Path.Combine(Folder, NOTES_FOLDER_NAME)))
            {
                Directory.Delete(Path.Combine(Folder, NOTES_FOLDER_NAME));
            }

            Dispose(true);
        }

        #endregion // Creation/Opening/Deleting Related Methods

        #region Constructor/Finalizer

        private SparseBinaryStorage(MonitorCollectionStorage monitors, MilestoneStorage milestones, TaskStorage tasks, CountDownTimerStorage countDowns, MonitoringGroupCollectionStorage monitoringGroups, BuyCollectionStorage buys, NoteCollectionStorage notes, StorageFile storageFile, LabelsStorage labels, string path, ICryptographicEngine cryptographicEngine)
        {
            Contract.Requires(monitors != null);
            Contract.Requires(milestones != null);
            Contract.Requires(tasks != null);
            Contract.Requires(countDowns != null);
            Contract.Requires(monitoringGroups != null);
            Contract.Requires(buys != null);
            Contract.Requires(notes != null);
            Contract.Requires(storageFile != null);
            Contract.Requires(labels != null);
            Contract.Requires(!String.IsNullOrEmpty(path));

            Folder = path;

            _cryptographicEngine = cryptographicEngine;
            _monitors = monitors;
            _milestones = milestones;
            _tasks = tasks;
            _countDowns = countDowns;
            _monitoringGroups = monitoringGroups;
            _buys = buys;
            _notes = notes;
            _storageFile = storageFile;
            _labels = labels;

            _login = _labels.GetLabel(_storageFile.LoginID);
            _password = _labels.GetLabel(_storageFile.PasswordID);
        }

        ~SparseBinaryStorage()
        {
            Dispose(false);
        }

        #endregion // Constructor/Finalizer

        #region Public Methods

        public double GetSummaryEfficiency()
        {
            double value = 0;
            int counter = 0;

            foreach (var monitor in Monitors)
            {
                if (monitor.Type == MonitorType.Monitor)
                {
                    value += monitor.GetAverageValue();
                    counter += 1;
                }
            }

            return counter > 0 ? value / (double)counter : 0;
        }

        #endregion // Public Methods

        #region IDisposable Members

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Dispose(true);
        }

        private void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _monitors.Dispose();
                    _monitors = null;

                    _milestones.Dispose();
                    _milestones = null;

                    _tasks.Dispose();
                    _tasks = null;

                    _countDowns.Dispose();
                    _countDowns = null;

                    _monitoringGroups.Dispose();
                    _monitoringGroups = null;

                    _buys.Dispose();
                    _buys = null;

                    _notes.Dispose();
                    _notes = null;

                    _storageFile.Dispose();
                    _storageFile = null;

                    _labels.Dispose();
                    _labels = null;
                }

                _disposed = true;
            }
        }

        #endregion

        #region Fields

        private bool _disposed;
        private MonitorCollectionStorage _monitors;
        private MilestoneStorage _milestones;
        private TaskStorage _tasks;
        private CountDownTimerStorage _countDowns;
        private MonitoringGroupCollectionStorage _monitoringGroups;
        private BuyCollectionStorage _buys;
        private NoteCollectionStorage _notes;
        private StorageFile _storageFile;
        private ICryptographicEngine _cryptographicEngine;
        private LabelsStorage _labels;
        private string _login;
        private string _password;

        #endregion // Fields
    }
}
